﻿using H.Eyes;
using H.Hands;
using H.Kernel.Utils;
using H.Logs;
using H.QuickDoes;
using H.Types;
using H.Utils;
using HModel;
using System;
using System.IO;
using System.Threading;


namespace H.Kernel.Login
{
    public class ClientLogin : ILogin
    {
        /// <summary>
        /// 表示快速操作的上下文信息
        /// </summary>
        private class Ctx : IQDContext
        {
            /// <summary>
            /// 登录器左上角坐标
            /// </summary>
            public Pos ClientLeftTopPos;

            /// <summary>
            /// 登录器窗口位置大小
            /// </summary>
            public Rect ClientRect;

            /// <summary>
            /// 密码输入错误的次数(如果超过3次,则必须要停止程序,不能无限尝试)
            /// </summary>
            public int PasswordWrongCount;
        }


        /// <summary>
        /// <inheritdoc/>
        /// 应设置为客户端启动器的路径,示例如:
        /// "D:\game\WeGameApps\地下城与勇士\TCLS\Client.exe"
        /// </summary>
        public string StarterPath { get; set; }

        ///<inheritdoc/>
        public string UserName { get; set; }

        ///<inheritdoc/>
        public string Password { get; set; }

        ///<inheritdoc/>
        public string Region { get; set; }

        ///<inheritdoc/>
        public string ErrorMessage { get; protected set; }

        //上下文对象
        private readonly ControllerContext _Context;

        /// <summary>
        /// 快速操作对象
        /// </summary>
        private readonly IQuickDo<Ctx> _QuickDo;

        /// <summary>
        /// 日志对象
        /// </summary>
        private readonly ILog Log;

        /// <summary>
        /// 是否已经初始化过
        /// </summary>
        private bool _Initialized = false;

        private const string RESOURCE_NAME = "GameClientLogin";

        public ClientLogin(ControllerContext ctx)
        {
            _Context = ctx;

            _QuickDo = _Context.QuickDoFactory.Value.CreateInstance(new Ctx());
            Log = _Context.Log.GetInstance("ClientLogin");
        }

        private enum Step
        {
            /// <summary>
            /// 检查游戏或客户端是否已经启动
            /// </summary>
            CheckGameRunning = 0,

            /// <summary>
            /// 查找游戏登录客户端
            /// </summary>
            StartGameLoginClient,

            /// <summary>
            /// 等待游戏登录器启动
            /// </summary>
            WaitForGameLoginClient,

            /// <summary>
            /// 查找游戏登录器窗口
            /// </summary>
            GetGameLoginClientWindow,

            /// <summary>
            /// 检查是否在更新状态
            /// </summary>
            CheckAutoUpdating,

            /// <summary>
            /// 确认选区(登录游戏按钮)
            /// </summary>
            ConfirmRegion,

            /// <summary>
            /// 检查是否需要进行游戏大区繁忙提醒的响应
            /// </summary>
            CheckBusyRegionConfirm,

            /// <summary>
            /// 等待安全检查结束
            /// </summary>
            WaitingSecurityCheck,

            /// <summary>
            /// 输入密码进行登录
            /// </summary>
            InputPassword,

            /// <summary>
            /// 检查许可协议是否勾选
            /// </summary>
            CheckPermission,

            /// <summary>
            /// 点击登录帐号按钮
            /// </summary>
            ClickLoginButton,

            /// <summary>
            /// 检查是否出现密码错误
            /// </summary>
            CheckPasswordWrong,

            /// <summary>
            /// 当执行过程中遇到无法解决的错误,比如密码错误,找不到路径之类问题时,统一从此Action处退出
            /// </summary>
            FailedExecution
        }


        /// <summary>
        /// 第一次运行时执行,用于对QuickDo进行初始化,和数据加载
        /// </summary>
        /// <returns></returns>
        private bool Init()
        {
            _Initialized = false;

            IEye eye = _Context.Eye;
            IHand hand = _Context.Hand;

            //资源加载器
            ResLoader resLoader = new ResLoader(_Context.Dao);


            //获取游戏客户端登录器的窗口位置
            ISee ClientPosFlag = eye.FindPicB(resLoader.LoadImgRes(RESOURCE_NAME, "PosFlag"));
            ISee TaskBarIcon = eye.FindPicB(resLoader.LoadImgRes(RESOURCE_NAME, "TaskBarIcon"));
            ISee AutoUpdatingIcon = eye.FindPic(resLoader.LoadImgRes(RESOURCE_NAME, "IsAutoUpdate"));
            ISee RegionWindow = eye.FindPic(resLoader.LoadImgRes(RESOURCE_NAME, "IsRegionWindow"));
            ISee IsRegionBusyFlag = eye.FindPicB(resLoader.LoadImgRes(RESOURCE_NAME, "IsRegionBusyFlag"));
            ISee InputWindow = eye.FindPic(resLoader.LoadImgRes(RESOURCE_NAME, "IsInputWindow"));
            ISee PermissionChecked = eye.FindPic(resLoader.LoadImgRes(RESOURCE_NAME, "PermissionChecked"));
            ISee SecurityCheckFinish = eye.FindPic(resLoader.LoadImgRes(RESOURCE_NAME, "SecurityCheckFinish"));
            ISee PasswordWrongFlag = eye.FindPicB(resLoader.LoadImgRes(RESOURCE_NAME, "PasswordWrongFlag"));
            ISee NeedVerifyCodeFlag = eye.FindPicB(resLoader.LoadImgRes(RESOURCE_NAME, "NeedVerifyCodeFlag"));


            ISee FindWhiteColor = eye.FindColor(new RGB(0xFFFFFF));

            Rect LoginGameButtonCheckRect = Rect.FromLTRB(700, 410, 927, 526);
            Rect LoginGameButtonClickRect = Rect.FromLTRB(774, 487, 865, 506);
            Rect PasswordRect = Rect.FromLTRB(1028, 364, 1111, 377);
            Rect PermissionCheckRect = Rect.FromLTRB(925, 392, 955, 423);
            Rect PermissionClickRect = Rect.FromLTRB(938, 406, 949, 417);
            Rect ClickButtonRect = Rect.FromLTRB(996, 490, 1090, 510);
            Rect ConfirmPasswordWrongRect = Rect.FromLTRB(160, 155, 198, 175);//基于窗口的偏移

            Rect VerifyCodeInputRect = Rect.FromLTRB(92, 122, 147, 140);

            //全屏区域的矩形
            Rect fullRect = RectUtils.GetFullScreenRect();
            Rect taskRect = RectUtils.GetTaskBarRect();


            IMouse LeftMouse = hand.LeftMouse();
            IKeyBoard CtrlAKey = hand.CreateKeys(HandKey.Ctrl, HandKey.A);
            IKeyBoard BackspaceKey = hand.CreateKey(HandKey.Backspace);

            //检查游戏是否已经启动,如未启动则进行启动
            _QuickDo.SetAction((int)Step.CheckGameRunning, qd =>
             {
                 qd.Context.PasswordWrongCount = 0;
                 if (ProcessUtil.Exists("dnf.exe"))
                 {
                     ErrorMessage = "游戏已经启动";
                     return (int)Step.FailedExecution;
                 }
                 else if (ProcessUtil.Exists("client.exe"))
                 {
                     ProcessUtil.Kill("client.exe");
                     qd.Delay(1500, 3000);
                     return (int)Step.CheckGameRunning;
                 }
                 else
                 {
                     if (!File.Exists(StarterPath))
                     {
                         ErrorMessage = "游戏登录器配置错误";
                         return (int)Step.FailedExecution;
                     }

                     return (int)Step.StartGameLoginClient;
                 }
             }, 0, null);

            //写入区服信息,并启动
            _QuickDo.SetAction((int)Step.StartGameLoginClient, qd =>
            {
                GameRegionEntity[] region = _Context.Dao.Query<GameRegionEntity>().Where("name", "=", Region).Select();
                if (region.Length > 0)
                {
                    //启动配置文件
                    string iniPath = Path.Combine(Path.GetDirectoryName(StarterPath), @"config\LoginQ.dat");
                    INIUtil.WriteInt(iniPath, "NoLoginUserRecord", "LastLogin", region[0].Code);
                    INIUtil.WriteString(iniPath, "Public", "LastQQUin", UserName);

                    Log.Info($"已写入区服信息:{Region}({region[0].Code}) 用户名为:{UserName}");
                    ProcessUtil.Start(StarterPath);
                    return (int)Step.WaitForGameLoginClient;
                }
                else
                {
                    Log.Error($"指定的区服不存在:{Region}");
                    ErrorMessage = "指定的区服不存在";
                    return false;
                }
            }, 0, null);

            //等待游戏登录器启动
            _QuickDo.SetAction((int)Step.WaitForGameLoginClient, qd =>
            {
                if (ProcessUtil.Exists("Client.exe"))
                {
                    return (int)Step.GetGameLoginClientWindow;
                }
                else
                {
                    qd.Delay(3000);
                    return (int)Step.CheckGameRunning;
                }
            }, 100, null);

            //等待找到游戏启动器界面
            _QuickDo.SetAction((int)Step.GetGameLoginClientWindow, qd =>
            {
                qd.Delay(1000);

                Log.Debug("正在查找游戏登录客户端");

                //能够找到游戏客户端的图片
                if (ClientPosFlag.See(fullRect, out qd.Context.ClientLeftTopPos) != -1)
                {
                    Log.Debug("找到游戏客户端坐标:" + qd.Context.ClientLeftTopPos);
                    qd.Context.ClientRect = new Rect(qd.Context.ClientLeftTopPos, 1200, 690);
                    if (RegionWindow.See(qd.Context.ClientRect))
                    {
                        return (int)Step.ConfirmRegion;
                    }
                    else if (InputWindow.See(qd.Context.ClientRect))
                    {
                        return (int)Step.WaitingSecurityCheck;
                    }
                    else if (AutoUpdatingIcon.See(qd.Context.ClientRect))
                    {
                        return (int)Step.CheckAutoUpdating;
                    }
                }

                if (TaskBarIcon.See(taskRect, out Pos pos) != -1)
                {
                    Log.Debug("没有找到游戏客户端,找到游戏客户端任务栏,正在激活游戏客户端窗口");
                    pos.Offset(10, 10);
                    LeftMouse.Click(pos);
                    qd.Delay(1500, 2500);
                }
                return false;
            }, 10, (int)Step.WaitForGameLoginClient);

            //检查是否在更新状态,如果是,则进行等待
            _QuickDo.SetAction((int)Step.CheckAutoUpdating, qd =>
            {
                if (AutoUpdatingIcon.See(qd.Context.ClientRect))
                {
                    Log.Debug("正在等待客户端自动更新");
                    qd.Delay(4000, 6000);
                    return false;
                }
                else
                {
                    return (int)Step.ConfirmRegion;
                }
            }, 200, (int)Step.GetGameLoginClientWindow);

            //点击登录游戏按钮
            _QuickDo.SetAction((int)Step.ConfirmRegion, qd =>
            {
                Rect rect = LoginGameButtonCheckRect;
                rect.Offset(qd.Context.ClientLeftTopPos);
                if (FindWhiteColor.See(rect))
                {
                    Log.Debug("正在点击登录游戏按钮");
                    qd.Delay(800, 1200);
                    rect = LoginGameButtonClickRect;
                    rect.Offset(qd.Context.ClientLeftTopPos);
                    LeftMouse.Click(rect);
                    qd.Delay(1200, 2000);
                    return (int)Step.CheckBusyRegionConfirm;
                }
                else
                {
                    return false;
                }
            }, 20, (int)Step.GetGameLoginClientWindow);

            //检查是否出现服务器繁忙的对话框
            _QuickDo.SetAction((int)Step.CheckBusyRegionConfirm, qd =>
             {
                 //正在检查是否出现服务器繁忙对话框,有则进行处理
                 //if ()
                 //{

                 //}
                 return (int)Step.GetGameLoginClientWindow;
             }, 5, (int)Step.GetGameLoginClientWindow);



            //等待输入密码
            _QuickDo.SetAction((int)Step.WaitingSecurityCheck, qd =>
            {
                Log.Debug("正在检查已经尝试输入密码的次数:" + qd.Context.PasswordWrongCount);
                if (qd.Context.PasswordWrongCount >= 2)
                {
                    ErrorMessage = "帐号密码错误,无法登录";
                    Log.Debug("重试登录三次皆失败,密码错误,即将停止");
                    return (int)Step.FailedExecution;
                }

                Log.Debug("正在等待安全检查结束");
                if (SecurityCheckFinish.See(qd.Context.ClientRect))
                {
                    return (int)Step.InputPassword;
                }
                else
                {
                    qd.Delay(1800, 2000);
                    return false;
                }
            }, 20, (int)Step.GetGameLoginClientWindow);

            //进行输入密码
            _QuickDo.SetAction((int)Step.InputPassword, qd =>
             {
                 Log.Debug("正在输入密码");
                 Rect rect = PasswordRect;
                 rect.Offset(qd.Context.ClientLeftTopPos);
                 LeftMouse.Click(rect);
                 if (FindWhiteColor.See(rect))
                 {
                     Log.Debug("密码框已输入密码,正在清空密码");
                     qd.Delay(400, 600);
                     CtrlAKey.Click();
                     qd.Delay(600, 900);
                     BackspaceKey.Click();
                     qd.Delay(400, 600);
                 }

                 hand.InputString(Password);
                 qd.Delay(800, 1200);
                 return (int)Step.CheckPermission;
             }, 0, (int)Step.WaitingSecurityCheck);

            //检查游戏协议是否已经勾选
            _QuickDo.SetAction((int)Step.CheckPermission, qd =>
             {
                 Rect rect = PermissionCheckRect;
                 rect.Offset(qd.Context.ClientLeftTopPos);
                 Log.Debug("正在检查游戏协议是否已经勾选");
                 if (!PermissionChecked.See(rect))
                 {
                     Log.Debug("正在同意勾选游戏协议");
                     rect = PermissionClickRect;
                     rect.Offset(qd.Context.ClientLeftTopPos);
                     LeftMouse.Click(rect);
                     return false;
                 }
                 else
                 {
                     return (int)Step.ClickLoginButton;
                 }
             }, 10, null);

            //点击登录按钮
            _QuickDo.SetAction((int)Step.ClickLoginButton, qd =>
             {
                 Log.Debug("正在点击登录按钮");
                 Rect rect = ClickButtonRect;
                 rect.Offset(qd.Context.ClientLeftTopPos);
                 LeftMouse.Click(rect);
                 return (int)Step.CheckPasswordWrong;
             }, 10, null);

            //检查是否出现密码错误,如果密码错误,则进行重试
            _QuickDo.SetAction((int)Step.CheckPasswordWrong, qd =>
            {
                Log.Debug("正在检查密码是否正确或者是否需要验证码");
                if (PasswordWrongFlag.See(qd.Context.ClientRect, out Pos pos) != -1)
                {
                    qd.Context.PasswordWrongCount++;
                    Rect rect = ConfirmPasswordWrongRect;
                    rect.Offset(pos);
                    LeftMouse.Click(rect);
                    return (int)Step.CheckPasswordWrong;
                }
                else if (NeedVerifyCodeFlag.See(qd.Context.ClientRect, out pos) != -1)
                {
                    ErrorMessage = "程序暂不支持验证码,请手动填入验证码";
                    return (int)Step.FailedExecution;
                }
                else
                {
                    return false;
                }
            }, 10, (int)Step.InputPassword);

            //用于强制退出
            _QuickDo.SetAction((int)Step.FailedExecution, qd =>
            {
                Log.Error("正在强制结束操作:" + ErrorMessage);
                return false;
            }, 0, null);

            //TODO:可能要对验证码进行检查

            _Initialized = true;
            return true;
        }

        ///<inheritdoc/>
        public bool Login(CancellationToken ct)
        {
            if (!_Initialized && !Init())
            {
                return false;
            }
            var ret = _QuickDo.DoAction((int)Step.CheckGameRunning);

            Log.Debug("运行结果:" + ret);

            return true;
        }
    }
}
